﻿using EdiNet;
using Xunit;
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace EdiNet.Test
{
    public class EdiStreamReaderTest : IUseFixture<EdiStreamReaderTest.EdiStreamReaderTestData>
    {
        #region TestData
        public class EdiStreamReaderTestData
        {
            public DataSegment TestIsa;
            public DataSegment TestGs;
            public DataSegment TestUnb;
            public EdiStreamReaderTestData()
            {
                TestIsa = new DataSegment("ISA");
                TestIsa[5].V = "ZZ";
                TestIsa[6].V = "SENDER";
                TestIsa[7].V = "ZZ";
                TestIsa[8].V = "RECEIVER";
                TestIsa[9].V = "110101";
                TestIsa[10].V = "1225";
                TestIsa[12].V = "00401";
                TestIsa[13].V = "000001234";
                TestIsa[15].V = "P";
                TestIsa[16].V = ">";
                DataSegment.FormatIsaSegment(TestIsa);
                TestGs = new DataSegment("GS");
                TestGs[1].V = "PO";
                TestGs[2].V = "SENDER";
                TestGs[3].V = "RECEIVER";
                TestGs[4].V = "20110101";
                TestGs[5].V = "1225";
                TestGs[6].V = "12345";
                TestGs[7].V = "X";
                TestGs[8].V = "004010";

                TestUnb = new DataSegment("UNB");
                TestUnb[1][0] = "IATB";
                TestUnb[1][1] = "1";
                TestUnb[2].V = "6XPPC";
                TestUnb[3].V = "LHPPC";
                TestUnb[4][0] = "940101";
                TestUnb[4][1] = "0950";
                TestUnb[5].V = "1";
            }
        }
        #endregion

        public const string ResourceBasePath = "Separators";
        public DataSegment TestIsa;
        public DataSegment TestGs;
        public DataSegment TestUnb;

        public void SetFixture(EdiStreamReaderTest.EdiStreamReaderTestData data)
        {
            this.TestIsa = data.TestIsa;
            this.TestGs = data.TestGs;
            this.TestUnb = data.TestUnb;
        }


        /// <summary>
        ///A test for EdiStreamReader Constructor
        ///</summary>
        [Fact()]
        public void EdiStreamReaderConstructorTest()
        {
            Stream ediStream = StreamHelper.GetResourceStream(ResourceBasePath, "CR.txt");
            EdiStreamReader target = new EdiStreamReader(ediStream);
        }

        /// <summary>
        ///A test for Close
        ///</summary>
        [Fact()]
        public void CloseTest()
        {
            Stream fs = StreamHelper.GetResourceStream(ResourceBasePath, "CR.txt");
            EdiStreamReader target = new EdiStreamReader(fs);
            target.ReadSegment();
            target.Close();
            Assert.Throws<ObjectDisposedException>(delegate()
            {
                fs.ReadByte(); //make sure that close method closes underlying streamreader
            });
        }

        /// <summary>
        ///A test for Dispose
        ///</summary>
        [Fact()]
        public void DisposeTest()
        {
            Stream fs = StreamHelper.GetResourceStream(ResourceBasePath, "CR.txt"); ;
            EdiStreamReader target = new EdiStreamReader(fs);
            target.ReadSegment();
            target.Dispose();
            Assert.Throws<ObjectDisposedException>(delegate()
            {
                fs.ReadByte(); //make sure that close method closes underlying streamreader
            });
        }


        /// <summary>
        ///A test for ReadSegment
        ///</summary>
        [Fact()]
        public void ReadSegmentTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "T.txt"));
            DataSegment isa = target.ReadSegment();
            DataSegment gs = target.ReadSegment();
            Assert.Equal(TestIsa, isa);
            Assert.Equal(TestGs, gs);
        }

        /// <summary>
        ///A test for Seek
        ///</summary>
        [Fact()]
        public void SeekTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "T.txt"));

            target.ReadSegment(); //isa
            long position = target.Position;
            target.ReadSegment();
            target.ReadSegment(); //read a couple of segments
            target.Seek(position);
            DataSegment gsSeg = target.ReadSegment();
            Assert.Equal(TestGs, gsSeg);
        }

        /// <summary>
        ///A test for EdiReaderWriterSettings
        ///</summary>
        [Fact()]
        public void EdiReaderWriterSettingsTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "TLF.txt"));
            target.ReadSegment();

            char dataElementSeparator = '*';
            //            char repetitionSeparator = '\0';
            char componentSeparator = '>';
            char segmentTerminator = '~';
            //            char releaseIndicator = ' ';
            SegmentTerminatorSuffix segmentTerminatorSuffix = SegmentTerminatorSuffix.LF;
            EdiStandard standard = EdiStandard.X12;

            EdiReaderWriterSettings s = target.CurrentEdiReaderWriterSettings;
            Assert.Equal(s.DataElementSeparatorChar, dataElementSeparator);
            //            Assert.Equal(s.RepetitionSeparatorChar, repetitionSeparator);
            Assert.Equal(s.ComponentSeparatorChar, componentSeparator);
            Assert.Equal(s.SegmentTerminatorChar, segmentTerminator);
            Assert.Equal(s.Suffix, segmentTerminatorSuffix);
            Assert.Equal(s.Standard, standard);
            //           Assert.Equal(s.ReleaseIndicatorChar, releaseIndicator);
        }

        [Fact()]
        public void EDIFACTEdiReaderWriterSettingsTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "UNA.txt"));
            target.ReadSegment();

            EdiReaderWriterSettings s = target.CurrentEdiReaderWriterSettings;
            Assert.Equal(s.Standard, EdiStandard.EDIFACT);

            Assert.Equal('1', s.ComponentSeparatorChar);
            Assert.Equal('2', s.DataElementSeparatorChar);
            Assert.Equal('4', s.ReleaseIndicatorChar);
            Assert.Equal('6', s.SegmentTerminatorChar);
            Assert.Equal(SegmentTerminatorSuffix.CRLF, s.Suffix);
        }

        [Fact()]
        public void ReadUnbUnhTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "NO_UNA.txt"));
            DataSegment UNB = target.ReadSegment();

            EdiReaderWriterSettings s = target.CurrentEdiReaderWriterSettings;
            Assert.ReferenceEquals(s, EdiReaderWriterSettings.DefaultEdifact);
            Assert.Equal(TestUnb, UNB);

            DataSegment UNH = target.ReadSegment();
            Assert.Equal("UNH", UNH.SegId);
        }



        /// <summary>
        ///A test for Position
        ///</summary>
        [Fact()]
        public void PositionTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "T.txt"));
            target.ReadSegment();
            Assert.Equal(106, target.Position);
        }

        /// <summary>
        ///A test for Position
        ///</summary>
        [Fact()]
        public void ReadCompositeTest()
        {
            EdiStreamReader target = new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, "COMPOSITE_ELEMENT.txt"));
            target.ReadSegment(); //ISA
            DataSegment seg = target.ReadSegment();
            Assert.Equal(9, seg.Count());

            Assert.True(seg[1].IsSimpleDataElement());
            Assert.True(seg[2].IsSimpleDataElement());
            Assert.False(seg[3].IsSimpleDataElement());
            Assert.False(seg[4].IsSimpleDataElement());
            Assert.False(seg[5].IsSimpleDataElement());
            Assert.False(seg[6].IsSimpleDataElement());
            Assert.False(seg[7].IsSimpleDataElement());
            Assert.False(seg[8].IsSimpleDataElement());

            Assert.Equal("25", seg[3][1]);
            Assert.Equal(string.Empty, seg[4][0]);
            Assert.Equal("17", seg[4][1]);
            Assert.Equal(string.Empty, seg[5][0]);
            Assert.Equal(string.Empty, seg[5][1]);
            Assert.Equal("12", seg[5][2]);

        }

        /// <summary>
        /// Separators test, all files must produce the same results
        /// </summary>
        [Fact()]
        public void SeparatorsTest()
        {
            string[] files = new string[] { "CR.txt", "LF.txt", "T.txt", "TCR.txt", "TCR_SPACES_AFTER_IEA.txt", "TCRLF.txt", "TLF.txt", "T_NEGATIVEICN.txt", "MULTIPLE_ISA.txt" };
            List<EdiStreamReader> readers = new List<EdiStreamReader>();
            //use different buffer sizes to test boundary conditions
            int bufferSize = 4;
            foreach (string file in files)
            {
                readers.Add(new EdiStreamReader(StreamHelper.GetResourceStream(ResourceBasePath, file), DefaultEdiEncoding.Encoding, bufferSize));
                bufferSize += 3;
                if (bufferSize > 15 && bufferSize < 200)
                    bufferSize += 100;
            }

            EdiStreamReader rd0 = readers[0];

            DataSegment seg = rd0.ReadSegment();
            while (null != seg)
            {
                for (int i = 1; i < readers.Count; ++i)
                {
                    DataSegment seg2 = readers[i].ReadSegment();
                    if (seg.SegId == "ISA" && files[i] == "T_NEGATIVEICN.txt")
                    {
                        Assert.Equal("-000001234", seg2[13].V);
                        seg2[13].V = "000001234";
                    }
                    //, files[0] + " vs " + files[i]
                    Assert.Equal(seg, seg2);
                }
                seg = rd0.ReadSegment();
            }
            for (int i = 1; i < readers.Count; ++i)
            {
                DataSegment seg3 = readers[i].ReadSegment();
                //multi isa should get ISA here
                if (files[i] == "MULTIPLE_ISA.txt")
                {
                    Assert.Equal("ISA", seg3.SegId);
                    continue;
                }
                Assert.Null(seg3);
                seg3 = readers[i].ReadSegment();
                Assert.Null(seg3);
            }


        }


        /// <summary>
        /// Separators test, all files must produce the same results
        /// </summary>
        [Fact()]
        public void MultiIsaTest()
        {
            EdiStreamReader multiIsaRd = new EdiStreamReader(
                StreamHelper.GetResourceStream(ResourceBasePath, "MULTIPLE_ISA.txt"), DefaultEdiEncoding.Encoding, 121);

            EdiStreamReader stdRd = new EdiStreamReader(
                StreamHelper.GetResourceStream(ResourceBasePath, "T.txt"), DefaultEdiEncoding.Encoding, 121);

            DataSegment seg = stdRd.ReadSegment();
            while (null != seg)
            {
                DataSegment seg2 = multiIsaRd.ReadSegment();
                Assert.Equal(seg, seg2);

                seg = stdRd.ReadSegment();
            }

            stdRd.Seek(0);
            seg = stdRd.ReadSegment();
            while (null != seg)
            {
                DataSegment seg2 = multiIsaRd.ReadSegment();
                Assert.Equal(seg, seg2);

                seg = stdRd.ReadSegment();
            }
        }

        [Fact()]
        public void GuessStandardTest_UNRECOGNIZED()
        {
            Assert.Throws<EdiParseException>(delegate()
            {
                new EdiStreamReader(StreamHelper.CreateStreamFromString("AAA*AAA")).ReadSegment();
            });
        }
    }
}
